#ifndef SLEPDEF_H
#define SLEPDEF_H


#include <math.h>
typedef enum{
	SlepLeft,
	SlepRight
}slep_side;

typedef enum{
	SlepCompressedSparseColumn,
	SlepCompressedSparseRow
}slep_sparse_format;

typedef enum{
	SlepSparse,
	SlepDense,
	SlepSymmetric,
	SlepTriangular,
	SlepBanded
}slep_sparsity;

typedef enum{
	SlepTranspose,
	SlepNoTranspose
}slep_transposity;

typedef enum{
	SlepUpper,
	SlepLower,
	SlepBandedUpper,
	SlepBandedLower,
	SlepBandedBoth,
	SlepFull
}slep_uplobanded;

typedef struct{
	double *values;
	int *ColumnsORRows;
	int *Index;
	slep_sparse_format format;
}sparse_matrix_base; // Zero Based Indexing Compressed-Sparse-Row Format

typedef struct{
	union{
		sparse_matrix_base spmtrx;
		double *mtrx;
	}data;

	/* This has value in {SlepDense, SlepSparse, SlepSymmetric, SlepTriangular} */
	slep_sparsity sparsity;

	/* This has value in {SlepUpper, SlepLower, SlepBandedUpper, SlepBandedLower, SlepBandedBoth, SlepFull} */
	slep_uplobanded uplobanded;

	/* M Rows */
	int m;

	/* N Columns */
	int n;

	/* Leading Dimension (used for Dense Matrices and Symmetric or Banded Matrices) */
	int ld;

	/* Bandwidth */
	int kl;
	int ku;

}slep_matrix;

typedef struct{
	double *values;
	int *index;
	int nnz;
}sparse_vector_base;

typedef struct{
	union{
		sparse_vector_base spvctr;
		double *vctr;
	}data;

	/* This has value in {SlepDense, SlepSparse} */
	slep_sparsity sparsity;

	int inc;
	int dim;
}slep_vector;



slep_matrix* createCSCSlepMatrix(int m, int n, double *values, int *rows, int *columnIndex);
slep_matrix* createCSRSlepMatrix(int m, int n, double *values, int *columns, int *rowIndex);
slep_matrix* createDenseSlepMatrix(int m, int ld, int n, double *values);

slep_matrix* createSymmetricSlepMatrix(int n, double *values, slep_uplobanded uplobanded, int bandwidth);
slep_matrix* createTriangularSlepMatrix(int n, double *values, slep_uplobanded uplobanded, int bandwidth);

slep_vector* createSparseSlepVector(int nnz, int *index, double *values);
slep_vector* createDenseSlepVector(int dimension, double *values, int inc);

int slepFreeVector(slep_vector* vctr);
slep_vector* slepCopyToNewVector(slep_vector* vctr);

int slepFreeMatrix(slep_matrix* mtrx);
int findNnz(slep_matrix* mtrx);

/* Should Implement these methods */
slep_vector* slepAllocDenseVector(int size);
slep_vector* slepAllocSparseVector(int nnz);
slep_matrix* slepAllocDenseMatrix(int m,int n);
slep_matrix* slepAllocSparseMatrix(int m,int nnz);
slep_vector* slepAllocVector(int memory, slep_sparsity sparsity);


/*--------------------------- Copied from Old Slep.h File ---------------------------*/

typedef struct{
	double* x;
	double* funVal;
	double* ValueL;
	char type[20]; // strcpy the type here
	double c; // Used for logistic regression only
	int totIter; // Total Iterations that happened in this call
	int errorCode; // 0 for no error
}FUNVAL;

typedef enum{
	SlepOPT_x0 = 0x1,
	SlepOPT_c0 = 0x2,
	SlepOPT_maxIter = 0x4,
	SlepOPT_tol = 0x8,
	SlepOPT_mu = 0x10,
	SlepOPT_nu = 0x20,
	SlepOPT_rsL2 = 0x40,
	SlepOPT_indices = 0x80,
	SlepOPT_sWeight = 0x100,
	SlepOPT_X0 = 0x200,
	SlepOPT_q = 0x400,
	SlepOPT_gWeights = 0x800,
	SlepOPT_fusedPenalty = 0x1000,
	SlepOPT_rStartNum = 0x2000
}OPTS_REGISTRY;

typedef struct{

	OPTS_REGISTRY REGISTER;

	//Starting points
	slep_vector* x0; // Starting x
	slep_matrix* X0; // Starting X (matrix)
	slep_vector* c0; // Used in Logistic regression only
	double *mt_c0; // Used in mtLogistic regression only - c0 not used here
	double *mc_c0; // Used in mcLogistic regression only - c0 not used here

	int init; // Init takes values 0,1,2 default:2

	// Termination
	int maxIter;
	int rStartNum;
	double tol;
	int tFlag;

	//Normalization
	int nFlag;
	slep_vector* mu;
	slep_vector* nu;

	//Method
	int method;
	int mFlag;
	int lFlag;

	//Regularization
	int rFlag;
	double rsL2;
	double fusedPenalty;

	//Group and others
	int* ind;
	int ind_size;
	double q;
	slep_vector *gWeights;

	double sWeight[2]; // registry set to 'sWeight'
	int mc_k;
	// 2 more to come

}OPTS;

#define setREGISTER(opts,field) ((opts)->REGISTER=(OPTS_REGISTRY)(((opts)->REGISTER)|(field)))
#define unsetREGISTER(opts,field) ((opts)->REGISTER=(OPTS_REGISTRY)(((opts)->REGISTER)&(~(field))))

void initOPTS(OPTS *opt);

//int hasGot(OPTS *opt,OPTS_REGISTRY field);
#define hasGot(opts,field) ((((opts)->REGISTER)&(field))==(field))

//void setOPTx0(OPTS *opt,slep_vector* x0);
#define setOPTx0(opts,x0_v)	{\
								if(!hasGot(opts,SlepOPT_x0))\
									((opts)->REGISTER) |= SlepOPT_x0;\
								((opts)->x0) = (x0_v);\
							}

//void setOPTX0(OPTS *opt,slep_matrix* X0);
#define setOPTX0(opts,X0_v)	{\
								if(!hasGot(opts,SlepOPT_X0))\
									((opts)->REGISTER) |= SlepOPT_X0;\
								((opts)->X0) = (X0_v);\
							}


//void setOPTc0(OPTS *opt,double c0);
#define setOPTc0(opts,c0_v)	{\
								if(!hasGot(opts,SlepOPT_c0))\
									((opts)->REGISTER) |= SlepOPT_c0;\
								((opts)->c0) = (c0_v);\
							}

#define setOPTmu(opts,mu_v)	{\
								if(!hasGot(opts,SlepOPT_mu))\
									((opts)->REGISTER) |= SlepOPT_mu;\
								((opts)->mu) = (mu_v);\
							}


#define setOPTnu(opts,nu_v)	{\
								if(!hasGot(opts,SlepOPT_nu))\
									((opts)->REGISTER) |= SlepOPT_nu;\
								((opts)->nu) = (nu_v);\
							}

#define setOPTsWeight(opts,sWeight_v) {\
										if(!hasGot(opts,SlepOPT_sWeight))\
											((opts)->REGISTER) |= SlepOPT_sWeight;\
										((opts)->sWeight) = (sWeight_v);\
									}

#define setOPTind(opts,ind_v,ind_size_v)	{\
										if(!hasGot(opts,SlepOPT_indices))\
												((opts)->REGISTER) |= SlepOPT_indices;\
											((opts)->ind) = (ind_v);\
											((opts)->ind_size) = (ind_size_v);\
										}

#define setOPTq(opts,q_v)		{\
										if(!hasGot(opts,SlepOPT_q))\
											((opts)->REGISTER) |= SlepOPT_q;\
										((opts)->q) = (q_v);\
									}

#define setOPTgWeights(opts,sWeight_v) {\
										if(!hasGot(opts,SlepOPT_gWeights))\
											((opts)->REGISTER) |= SlepOPT_gWeights;\
										((opts)->gWeights) = (gWeights_v);\
									}


void sll_opts(OPTS* opts);
void initNormalization(slep_matrix* A, OPTS* opts);


/*----------END----------------- Copied from Old Slep.h File ------------END---------------*/
#endif //SLEPDEF_H